import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { JWT_SECRET_KEY } from './auth.jwt.secret';
import { IS_PUBLIC_KEY } from './public.decorator';

// CanActivate是一个守卫，会在请求被允许处理的时候调用该守卫；也就是请求发送过来的时候，先到守卫处理，处理完以后再到Controller处理业务逻辑
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private jwtService: JwtService,
  ) {}

  // 在请求处理具体的业务逻辑之前，进行该处理
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    // 如果isPublic是true，表示是公共方法，返回true，表示可以访问
    if (isPublic) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const token = getTokenFromHeader(request);

    // 如果token不存在，就是没有传token，就直接抛出未登录的异常
    if (!token) {
      throw new UnauthorizedException();
    }

    // 如果存在，就去 1.JWT中验证token的可用性，2.同时将token解析出来；所以就需要用到jwtService
    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: JWT_SECRET_KEY,
      });
      request['user'] = payload; // 将payload放入request，请求头就会有该属性，之后在所有有校验鉴权的接口中，再去访问用户信息直接使用request.user即可
    } catch (err) {
      throw new UnauthorizedException();
    }

    // 如果都通过验证 返回true
    return true;
  }
}

function getTokenFromHeader(request) {
  const [type, token] = request.headers.authorization?.split(' ') ?? []; // '??'：表示如果为空，就返回空数组
  return type === 'Bearer' ? token : ''; // 防止他人伪造token
}
